
#if 0
 / {
      plat_demo: plat_demo {
          compatible = "plat_demo";
      };
 };
#endif

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>


#include <linux/fb.h>
#include <linux/notifier.h>

#include <linux/reboot.h>

#define COMPATILE_NAME "plat_demo"
// define PLAT_DEMO_FB_NOTIFY will cause system hang on
#define PLAT_DEMO_FB_NOTIFY
// register_restart_handler do nothing,
#define PLAT_DEMO_RESET_NB

// #define PLAT_DEMO_PM_OPS

struct plat_demo_priv {
	struct device *dev;
	// add what you want.
	int val; // just for test
};

#ifdef PLAT_DEMO_RESET_NB
// restart handler @{
static int plat_demo_reset_nb(struct notifier_block *self,
                       unsigned long action, void *data)
{
    printk("%s action=%lu, \n", __func__, action);
    return 0;
}
static struct notifier_block plat_demo_reset_notifier = {
    .notifier_call = plat_demo_reset_nb,
    .priority = 255
};
// @}
#endif
#ifdef PLAT_DEMO_FB_NOTIFY
// fb interface @{
static int plat_demo_fb_notify(struct notifier_block *self,
                       unsigned long action, void *data)
{

    struct fb_event *event = data;
    int blank_mode = 0;
    printk("%s enter\n", __func__);
    blank_mode = *((int *)event->data);
    switch (blank_mode) {
    case FB_BLANK_UNBLANK:
        printk("%s FB_BLANK_UNBLANK\n", __func__);
        break;
    case FB_BLANK_POWERDOWN:
    	printk("%s FB_BLANK_POWERDOWN\n", __func__);
    	break;
    default:
        printk("%s FB_BLANK_UNKNOW\n", __func__);
        break;
    }
    return 0;
}
static struct notifier_block plat_demo_fb_notifier = {
    .notifier_call = plat_demo_fb_notify,
};
// @}
#endif
#ifdef PLAT_DEMO_PM_OPS
static int pm_suspend(struct device *dev)
{
       dev_info(dev, "%s enter\n", __func__);
       return 0;
}

static int pm_resume(struct device *dev)
{
       dev_info(dev, "%s enter\n", __func__);
       return 0;
}

SIMPLE_DEV_PM_OPS(plat_demo_pm_ops, pm_suspend, pm_resume);
#endif
static ssize_t plat_demo_show(struct device *dev,struct device_attribute *attr, char *buf)
{
	int n=0;
	struct plat_demo_priv * priv = dev_get_drvdata(dev);
	dev_info(dev, "%s\n", __func__);
	n = sprintf(buf, "val=%d\n", priv->val);
	return n;
}

static ssize_t plat_demo_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t len)
{
	int op, n;
	struct plat_demo_priv * priv = dev_get_drvdata(dev);
	dev_info(dev, "%s buf=%s\n", __func__, buf);
	n = sscanf(buf, "%d", &op);
	if(n != 1){
		dev_err(dev, "Usage: echo 0~8 > debug\n");
	}else{
		priv->val = op;
	}
	return len;
}
static DEVICE_ATTR(debug, 0644, plat_demo_show, plat_demo_store);
static int plat_demo_probe(struct platform_device *pdev)
{
	struct plat_demo_priv *priv;
	int err = 0;
	dev_info(&pdev->dev, "%s enter\n", __func__);
	// store data
	if(NULL==(priv=kzalloc(sizeof(*priv), GFP_KERNEL))){
		dev_err(&pdev->dev, "kzalloc failed\n");
		return -ENOMEM;
	}
	platform_set_drvdata(pdev, priv);
	priv->dev = &pdev->dev;
	// create attribute file
	if((err=device_create_file(&pdev->dev, &dev_attr_debug))<0){
		dev_err(&pdev->dev, "device_create_file failed err=%d\n", err);
	}
#ifdef PLAT_DEMO_FB_NOTIFY
	fb_register_client(&plat_demo_fb_notifier);
#endif
#ifdef PLAT_DEMO_RESET_NB
	register_restart_handler(&plat_demo_reset_notifier);
#endif

	return 0;
}

static int plat_demo_remove(struct platform_device *pdev)
{
	dev_info(&pdev->dev, "%s enter\n", __func__);
#ifdef PLAT_DEMO_FB_NOTIFY
	fb_unregister_client(&plat_demo_fb_notifier);
#endif
	return 0;
}

static void plat_demo_shutdown(struct platform_device *pdev)
{
	dev_info(&pdev->dev, "%s enter\n", __func__);
}
static const struct of_device_id plat_demo_of_match[] = {
	{ .compatible = COMPATILE_NAME, },
	{},
};

int plat_demo_suspend(struct platform_device *pdev, pm_message_t state)
{
	// plat_demo plat_demo: plat_demo_suspend enter, event=2
	// include/linux/pm.h 2为PM_EVENT_SUSPEND
	dev_info(&pdev->dev, "%s enter, event=%d\n", __func__, state.event);
	return 0;
}
int plat_demo_resume(struct platform_device *pdev)
{
	// plat_demo plat_demo: plat_demo_resume enter
	dev_info(&pdev->dev, "%s enter\n", __func__);
	return 0;
}
static struct platform_driver plat_demo_driver = {
	.driver  = {
		.name    = COMPATILE_NAME,
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(plat_demo_of_match),
#ifdef PLAT_DEMO_PM_OPS
		.pm = &plat_demo_pm_ops,  // sleep and wakeup
#endif

	 },
	.probe   = plat_demo_probe,
	.remove  = plat_demo_remove,
    .shutdown = plat_demo_shutdown,  // system reboot and shutdown
    .suspend = plat_demo_suspend,
    .resume = plat_demo_resume,

};

static int __init plat_demo_init(void)
{
	pr_info("%s enter\n", __func__);
	platform_driver_register(&plat_demo_driver);
	return 0;
}

static void __init plat_demo_exit(void)
{
	pr_info("%s enter\n", __func__);
	platform_driver_unregister(&plat_demo_driver);
}

#if 0
// 使用此种方法，在注册fb_register_client会导致系统卡住，可能是注册的太早了，用下面的就没有问题
module_platform_driver(plat_demo_driver);
#else
late_initcall(plat_demo_init);
module_exit(plat_demo_exit);
#endif
MODULE_AUTHOR("tj <tj_wust@163.com>");
MODULE_DESCRIPTION("platform demo driver");
MODULE_LICENSE("GPL");
